package com.qd.panda.service.wallet.user;

import cn.hutool.core.math.Money;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cdqidi.constant.ConstantDto;
import com.cdqidi.db.BaseService;
import com.cdqidi.dto.ApiResultWrapper;
import com.cdqidi.dto.CacheKey;
import com.cdqidi.excel.ExcelExport;
import com.cdqidi.excel.util.ExportPage;
import com.cdqidi.exception.ApiException;
import com.cdqidi.util.DateUtil;
import com.cdqidi.util.RedisTemplateUtil;
import com.cdqidi.util.SpringContextHolder;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.qd.common.flow.constant.FlowConstants;
import com.qd.common.flow.domain.approval.FlowDTO;
import com.qd.common.flow.domain.approval.FlowDataDTO;
import com.qd.common.flow.domain.bill.FlowHisTaskDTO;
import com.qd.common.flow.domain.event.FlowTaskStateDTO;
import com.qd.common.panda.config.PandaParkConfigDTO;
import com.qd.common.panda.constant.PandaConstant;
import com.qd.common.panda.domain.entity.wallet.user.*;
import com.qd.common.sys.enums.OperatorType;
import com.qd.common.sys.util.SecurityUtils;
import com.qd.flow.service.bill.FlowHisTaskService;
import com.qd.flow.service.bill.FlowService;
import com.qd.panda.mapper.wallet.user.UserWalletRefundApplyMapper;
import com.qd.panda.model.UserWalletRefundApply;
import com.qd.panda.model.UserWalletRefundApplyFile;
import com.qd.panda.model.UserWalletRefundApplyOrder;
import com.qd.panda.model.UserWalletRefundLog;
import com.qd.panda.service.stroke.DisplayUtil;
import com.qd.pay.domain.OrderRefundDTO;
import com.qd.pay.service.order.OrderHolderService;
import com.qd.system.service.org.OrgService;
import com.qd.system.util.FileConfirmUtil;
import com.qd.upload.constant.FileConfirmDTO;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static java.util.stream.Collectors.toList;

/**
 * 用户退款申请
 *
 * @author sjk
 */
@RequiredArgsConstructor
@Service
@Slf4j
public class UserWalletRefundApplyService extends BaseService<UserWalletRefundApplyMapper, UserWalletRefundApply> {

    private static final String PREFIX_ID = "panda_user_wallet_refund_apply_requestId_{0}";
    private static final String PREFIX_APPLY = "wallet_refund_apply_requestId_{0}";

    private final UserWalletRefundApplyFileService userWalletRefundApplyFileService;
    private final UserWalletRefundApplyOrderService userWalletRefundApplyOrderService;
    private final UserWalletService userWalletService;
    private final UserWalletRechargeLogService userWalletRechargeLogService;
    private final UserWalletRefundLogService userWalletRefundLogService;
    private final OrgService orgService;
    private final FlowService flowService;
    private final OrderHolderService orderHolderService;
    private final ObjectMapper objectMapper;
    private final RedisTemplateUtil<UserWalletRefundApply> redisTemplateUtil;
    @Resource
    private final FlowHisTaskService flowHisTaskService;
    @Resource
    private final PandaParkConfigDTO pandaParkConfig;
    @Resource
    private HttpServletResponse response;

    /**
     * 保存
     *
     * @param dto 页面对象
     * @return 页面对象
     */
    @Transactional(rollbackFor = Exception.class)
    public UserWalletRefundApplyDTO save(UserWalletRefundApplyDTO dto) {
        final UserWalletRefundApply model = dtoToModel(dto);
        try {
            if (super.save(model)) {
                saveOrders(dto);
                createFlow(dto);
                return modelToDto(model);
            }
            throw new ApiException("添加失败");
        } finally {
            clearCache(model);
            model.freeData();
        }
    }

    /**
     * 修改
     *
     * @param dto 页面对象
     * @return 页面对象
     */
    @Transactional(rollbackFor = Exception.class)
    public UserWalletRefundApplyDTO update(UserWalletRefundApplyDTO dto, boolean isCreateFlow) {
        final UserWalletRefundApply historyModel = Optional.ofNullable(getById(dto.getRequestId())).orElseThrow(() -> new ApiException("ID不存在,不能更新"));
        final UserWalletRefundApply model = dtoToModel(dto);
        try {
            if (super.updateById(model)) {
                if (isCreateFlow) {
                    createFlow(dto);
                }
                return modelToDto(model);
            }
            throw new ApiException("更新失败");
        } finally {
            clearCache(historyModel);
            historyModel.freeData();
            model.freeData();
        }
    }

    /**
     * 批量添加
     *
     * @param mList 数据库对象
     */
    @Transactional(rollbackFor = Exception.class)
    public void saveBatch(List<UserWalletRefundApply> mList) {
        try {
            if (!super.saveBatch(mList)) {
                throw new ApiException("批量添加失败");
            }
        } finally {
            clearCaches(mList);
        }
    }

    /**
     * 批量更新
     *
     * @param mList 数据库对象
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateBatchIds(List<UserWalletRefundApply> mList) {
        final List<String> ids = mList.stream().map(UserWalletRefundApply::getRequestId).collect(toList());
        final List<UserWalletRefundApply> historyModelList = checkIds(ids);
        try {
            if (!super.updateBatchById(mList)) {
                throw new ApiException("批量更新失败");
            }
        } finally {
            clearCaches(historyModelList);
            historyModelList.clear();
            ids.clear();
        }
    }

    /**
     * 删除，单个
     *
     * @param requestId 主键
     */
    @Transactional(rollbackFor = Exception.class)
    public void removeById(String requestId) {
        final UserWalletRefundApply historyModel = Optional.ofNullable(getById(requestId)).orElseThrow(() -> new ApiException("数据不存在,不能删除"));
        try {
            if (!super.removeById(requestId)) {
                throw new ApiException("删除失败");
            }
        } finally {
            clearCache(historyModel);
            historyModel.freeData();
        }
    }

    /**
     * 批量删除
     *
     * @param ids 主键集合
     */
    @Transactional(rollbackFor = Exception.class)
    public void removeBatchIds(List<String> ids) {
        final List<UserWalletRefundApply> historyModelList = checkIds(ids);
        try {
            if (!super.removeByIds(ids)) {
                throw new ApiException("批量删除失败");
            }
        } finally {
            clearCaches(historyModelList);
            historyModelList.clear();
        }
    }

    /**
     * 根据传入对象构建查询参数，判断是否存在
     *
     * @param dto 参数
     * @return 存在为true
     */
    @Transactional(readOnly = true)
    public Boolean isExists(UserWalletRefundApplyDTO dto) {
        final QueryWrapper<UserWalletRefundApply> queryWrapper = createQueryWrapper(dto);
        final boolean exists = super.count(queryWrapper) > 0;
        queryWrapper.clear();
        return exists;
    }

    /**
     * 根据主键判断数据是否存在
     *
     * @param requestId 主键
     * @return 存在为true
     */
    @Transactional(readOnly = true)
    public Boolean isExistsById(String requestId) {
        boolean exists = false;
        if (StringUtils.hasLength(requestId)) {
            final UserWalletRefundApply model = getById(requestId);
            exists = (null != model);
            if (exists) {
                model.freeData();
            }
        }
        return exists;
    }

    /**
     * 根据主键获取数据
     *
     * @param requestId 主键
     * @return 单条记录
     */
    @Transactional(readOnly = true)
    public UserWalletRefundApply getById(String requestId) {
        final String key = RedisTemplateUtil.getRedisCacheKey(PREFIX_ID, requestId);
        return redisTemplateUtil.get(new CacheKey().setKey(key).setTimeout(ConstantDto.REDIS_TIME_OUT_DAY), () -> super.getById(requestId), UserWalletRefundApply.class);
    }

    /**
     * 根据主键获取数据
     *
     * @param requestId 主键
     * @return 单条记录，转换成页面对象
     */
    @Transactional(readOnly = true)
    public UserWalletRefundApplyDTO getByIdDto(String requestId) {
        return Optional.ofNullable(getById(requestId)).map(this::modelToDto).orElse(null);
    }

    /**
     * 根据条件查询单条数据
     *
     * @param dto 查询参数
     * @return 单条记录
     */
    @Transactional(readOnly = true)
    public UserWalletRefundApply getOne(UserWalletRefundApplyDTO dto) {
        final QueryWrapper<UserWalletRefundApply> queryWrapper = createQueryWrapper(dto);
        UserWalletRefundApply model = super.getOne(queryWrapper);
        queryWrapper.clear();
        return model;
    }

    /**
     * 根据条件查询单条数据
     *
     * @param dto 查询参数
     * @return 单条记录，转换成页面对象
     */
    @Transactional(readOnly = true)
    public UserWalletRefundApplyDTO getOneDto(UserWalletRefundApplyDTO dto) {
        return Optional.ofNullable(getOne(dto)).map(this::modelToDto).orElse(null);
    }

    /**
     * 根据条件查询列表数据
     *
     * @param dto 查询参数
     * @return 列表数据
     */
    @Transactional(readOnly = true)
    public List<UserWalletRefundApply> getList(UserWalletRefundApplyDTO dto) {
        final QueryWrapper<UserWalletRefundApply> queryWrapper = createQueryWrapper(dto);
        final List<UserWalletRefundApply> list = super.list(queryWrapper);
        queryWrapper.clear();
        return list;
    }

    /**
     * 根据条件查询列表数据
     *
     * @param dto 查询参数
     * @return 列表数据，转换成页面对象
     */
    @Transactional(readOnly = true)
    public List<UserWalletRefundApplyDTO> getListDto(UserWalletRefundApplyDTO dto) {
        return modelToDtoList(getList(dto));
    }

    /**
     * 分页数据
     *
     * @param page 分页对象
     * @param dto  查询参数
     * @return 分页信息
     */
    @Transactional(readOnly = true)
    public IPage<UserWalletRefundApplyDTO> page(Page<UserWalletRefundApply> page, UserWalletRefundApplyDTO dto) {
        final QueryWrapper<UserWalletRefundApply> queryWrapper = createQueryWrapper(dto, "t1.");
        queryWrapper.orderByDesc("t1.request_time");
        final IPage<UserWalletRefundApplyDTO> iPage = baseMapper.page(page, queryWrapper);
        Optional.ofNullable(iPage.getRecords()).ifPresent(l -> l.forEach(data -> buildDisplay(data, false)));
        queryWrapper.clear();
        return iPage;
    }

    /**
     * 导出Excel
     *
     * @param query 查询条件
     */
    @Transactional(readOnly = true)
    public void export(UserWalletRefundApplyDTO query) {
        final ExportPage<UserWalletRefundApply, UserWalletRefundApplyDTO> exportPage = new ExportPage<>();
        try {
            final List<UserWalletRefundApplyDTO> list = exportPage.getAllForPage(page -> page(page, query).getRecords());
            try {
                ExcelExport.downloadExcel(response, UserWalletRefundApplyDTO.class, () -> list);
            } catch (IOException e) {
                log.error("导出失败", e);
                throw new ApiException("导出失败");
            }
        } finally {
            exportPage.freeData();
        }
    }

    /**
     * 分页数据
     *
     * @param page 分页对象
     * @param dto  查询参数
     * @return 分页信息
     */
    @Transactional(readOnly = true)
    public IPage<UserWalletRefundApplyDTO> applyRefundPage(Page<UserWalletRefundApply> page, UserWalletRefundApplyDTO dto) {
        final QueryWrapper<UserWalletRefundApply> queryWrapper = createQueryWrapper(dto, "t1.");
        queryWrapper.orderByDesc("t1.request_time");
        final IPage<UserWalletRefundApplyDTO> iPage = baseMapper.page(page, queryWrapper);
        Optional.ofNullable(iPage.getRecords()).ifPresent(l -> l.forEach(data -> buildDisplay(data, true)));
        queryWrapper.clear();
        return iPage;
    }

    @Transactional(readOnly = true)
    public IPage<FlowDataDTO<UserWalletRefundApplyDTO>> refundApprovedPage(Page<UserWalletRefundApply> page, UserWalletRefundApplyDTO dto) {
        final QueryWrapper<UserWalletRefundApply> queryWrapper = createQueryWrapper(dto, "t1.");
        queryWrapper.eq("t3.approval", SecurityUtils.getLoginUser().getUserId());
        queryWrapper.orderByDesc("t1.request_time");
        final IPage<FlowDataDTO<UserWalletRefundApplyDTO>> iPage = baseMapper.refundApprovedPage(page, queryWrapper);
        Optional.ofNullable(iPage.getRecords()).ifPresent(l -> l.forEach(data -> buildDisplay(data.getData(), false)));
        queryWrapper.clear();
        return iPage;
    }

    /**
     * 清空缓存
     *
     * @param model 数据库对象
     */
    public void clearCache(UserWalletRefundApply model) {
        if (null == model) {
            return;
        }
        redisTemplateUtil.deleteByKey(RedisTemplateUtil.getRedisCacheKey(PREFIX_ID, model.getRequestId()));
    }

    /**
     * 批量清空缓存
     *
     * @param list 数据库对象
     */
    public void clearCaches(List<UserWalletRefundApply> list) {
        if (null == list || list.isEmpty()) {
            return;
        }
        List<String> ids = list.stream().map(model -> RedisTemplateUtil.getRedisCacheKey(PREFIX_ID, model.getRequestId())).collect(toList());
        if (!ids.isEmpty()) {
            redisTemplateUtil.deleteByKey(ids);
            ids.clear();
        }
    }

    /**
     * 数据库对象转页面对象
     *
     * @param model 数据库对象
     * @return 页面对象
     */
    public UserWalletRefundApplyDTO modelToDto(UserWalletRefundApply model) {
        if (null == model) {
            return null;
        }
        final UserWalletRefundApplyDTO dto = new UserWalletRefundApplyDTO();
        dto.setRequestId(model.getRequestId());
        dto.setRequestRefundAmount(model.getRequestRefundAmount());
        dto.setRequestRefundGiveAmount(model.getRequestRefundGiveAmount());
        dto.setActualRefundAmount(model.getActualRefundAmount());
        dto.setActualRefundGiveAmount(model.getActualRefundGiveAmount());
        dto.setRequestTime(model.getRequestTime());
        dto.setRequestMobile(model.getRequestMobile());
        dto.setRefundMethod(model.getRefundMethod());
        dto.setOpenBank(model.getOpenBank());
        dto.setBankCard(model.getBankCard());
        dto.setApprovalStatus(model.getApprovalStatus());
        dto.setCompletionTime(model.getCompletionTime());
        dto.setApplicant(model.getApplicant());
        buildDisplay(dto, false);
        return dto;
    }

    /**
     * 页面对象转数据库对象
     *
     * @param dto 页面对象
     * @return 数据库对象
     */
    public UserWalletRefundApply dtoToModel(UserWalletRefundApplyDTO dto) {
        if (null == dto) {
            return null;
        }
        final UserWalletRefundApply model = new UserWalletRefundApply();
        model.setRequestId(dto.getRequestId());
        model.setRequestRefundAmount(dto.getRequestRefundAmount());
        model.setRequestRefundGiveAmount(dto.getRequestRefundGiveAmount());
        model.setActualRefundAmount(dto.getActualRefundAmount());
        model.setActualRefundGiveAmount(dto.getActualRefundGiveAmount());
        model.setRequestTime(dto.getRequestTime());
        model.setRequestMobile(dto.getRequestMobile());
        model.setRefundMethod(dto.getRefundMethod());
        model.setOpenBank(dto.getOpenBank());
        model.setBankCard(dto.getBankCard());
        model.setApprovalStatus(dto.getApprovalStatus());
        model.setCompletionTime(dto.getCompletionTime());
        model.setApplicant(dto.getApplicant());
        return model;
    }

    /**
     * 码表转换
     *
     * @param dto 页面对象
     */
    public void buildDisplay(UserWalletRefundApplyDTO dto, boolean flowHis) {
        if (flowHis) {
            if (dto.getApprovalStatus() == FlowConstants.APPROVAL_STATUS_2 || dto.getApprovalStatus() == FlowConstants.APPROVAL_STATUS_3) {
                List<FlowHisTaskDTO> flowHisList = flowHisTaskService.getListByBid(dto.getRequestId(), null);
                if (!flowHisList.isEmpty()) {
                    int size = flowHisList.size();
                    FlowHisTaskDTO flowHisTaskDTO = flowHisList.get(size - 1);
                    ObjectNode objectNode = objectMapper.createObjectNode();
                    objectNode.put("approval", flowHisTaskDTO.getStatus());
                    objectNode.put("result", flowHisTaskDTO.getResult());
                    objectNode.put("addTime", DateUtil.localDateTimeToStr(flowHisTaskDTO.getAddTime()));
                    dto.setLastFlowHis(objectNode);
                }
            }
        }
        if (null != dto.getApprovalStatus()) {
            if (dto.getApprovalStatus().equals(FlowConstants.APPROVAL_STATUS_0)) {
                dto.setApprovalStatusDisplay("审批中");
            }
            if (dto.getApprovalStatus().equals(FlowConstants.APPROVAL_STATUS_1)) {
                dto.setApprovalStatusDisplay("审批通过");
            }
            if (dto.getApprovalStatus().equals(FlowConstants.APPROVAL_STATUS_2)) {
                dto.setApprovalStatusDisplay("审批未通过");
            }
            if (dto.getApprovalStatus().equals(FlowConstants.APPROVAL_STATUS_3)) {
                dto.setApprovalStatusDisplay("审批被驳回");
            }
            if (!flowHis && (dto.getApprovalStatus() == FlowConstants.APPROVAL_STATUS_1) && dto.getRefundMethod() == 2) {
                List<UserWalletRefundApplyFileDTO> list = userWalletRefundApplyFileService.getListByRequestId(dto.getRequestId());
                if (!list.isEmpty()) {
                    dto.setFileList(list);
                }
            }
        }
        if (null != dto.getRefundMethod()) {
            if (dto.getRefundMethod() == 1) {
                dto.setRefundMethodDisplay("原路退回");
            }
            if (dto.getRefundMethod() == 2) {
                dto.setRefundMethodDisplay("线下处理");
            }
        }
    }

    /**
     * 数据库对象转页面对象
     *
     * @param list 数据库对象
     * @return 页面对象
     */
    public List<UserWalletRefundApplyDTO> modelToDtoList(List<UserWalletRefundApply> list) {
        return Optional.ofNullable(list).orElse(Collections.emptyList()).stream().map(this::modelToDto).collect(toList());
    }

    /**
     * 页面对象转数据库对象
     *
     * @param list 页面对象
     * @return 数据库对象
     */
    public List<UserWalletRefundApply> dtoToModelList(List<UserWalletRefundApplyDTO> list) {
        return Optional.ofNullable(list).orElse(Collections.emptyList()).stream().map(this::dtoToModel).collect(toList());
    }

    /**
     * 拼装查询参数，除了主键的其他字段会自动加入进来
     *
     * @param dto 查询参数，不为空的属性会被自动加入
     */
    public QueryWrapper<UserWalletRefundApply> createQueryWrapper(UserWalletRefundApplyDTO dto) {
        return createQueryWrapper(dto, "");
    }

    /**
     * 拼装查询参数，除了主键的其他字段会自动加入进来
     *
     * @param dto        查询参数，不为空的属性会被自动加入
     * @param tableAlias 表别名
     */
    public QueryWrapper<UserWalletRefundApply> createQueryWrapper(UserWalletRefundApplyDTO dto, String tableAlias) {
        final QueryWrapper<UserWalletRefundApply> queryWrapper = new QueryWrapper<>();
        if (null != dto) {
            if (StringUtils.hasLength(tableAlias) && tableAlias.charAt(tableAlias.length() - 1) != '.') {
                tableAlias += ".";
            }
            if (StringUtils.hasLength(dto.getRequestMobile())) {
                queryWrapper.eq(tableAlias + "request_mobile", dto.getRequestMobile());
            }
            if (StringUtils.hasLength(dto.getOpenBank())) {
                queryWrapper.eq(tableAlias + "open_bank", dto.getOpenBank());
            }
            if (StringUtils.hasLength(dto.getBankCard())) {
                queryWrapper.eq(tableAlias + "bank_card", dto.getBankCard());
            }
            if (StringUtils.hasLength(dto.getApplicant())) {
                queryWrapper.eq(tableAlias + "applicant", dto.getApplicant());
            }
            if (null != dto.getRefundMethod()) {
                queryWrapper.eq(tableAlias + "refund_method", dto.getRefundMethod());
            }
            if (null != dto.getApprovalStatus()) {
                queryWrapper.eq(tableAlias + "approval_status", dto.getApprovalStatus());
            }
            if (null != dto.getApprovalStatusArray()) {
                queryWrapper.in(tableAlias + "approval_status", dto.getApprovalStatusArray());
            }
            if (StringUtils.hasLength(dto.getBeginTime()) && !StringUtils.hasLength(dto.getEndTime())) {
                queryWrapper.between(tableAlias + "request_time", dto.getBeginTime() + " 00:00:00", dto.getBeginTime() + " 23:59:59");
            }
            if (!StringUtils.hasLength(dto.getBeginTime()) && StringUtils.hasLength(dto.getEndTime())) {
                queryWrapper.between(tableAlias + "request_time", dto.getEndTime() + " 00:00:00", dto.getEndTime() + " 23:59:59");
            }
            if (StringUtils.hasLength(dto.getBeginTime()) && StringUtils.hasLength(dto.getEndTime())) {
                queryWrapper.between(tableAlias + "request_time", dto.getBeginTime() + " 00:00:00", dto.getEndTime() + " 23:59:59");
            }
        }
        return queryWrapper;
    }

    /**
     * 批量检查主键在数据库中是否存在
     *
     * @param ids 主键集合
     * @return 主键查询出来的集合
     */
    public List<UserWalletRefundApply> checkIds(List<String> ids) {
        if (null == ids || ids.isEmpty()) {
            throw new ApiException("ids不能为空");
        }
        final List<UserWalletRefundApply> list = super.listByIds(ids);
        if (list.isEmpty()) {
            throw new ApiException("ids不存在" + Arrays.toString(ids.toArray()));
        }
        if (ids.size() == list.size()) {
            return list;
        }
        List<String> dbIds = list.stream().map(UserWalletRefundApply::getRequestId).collect(toList());
        final List<String> notExistsList = ids.stream().filter(id -> !dbIds.contains(id)).collect(toList());
        dbIds.clear();
        if (!notExistsList.isEmpty()) {
            throw new ApiException("ids不存在" + Arrays.toString(notExistsList.toArray()));
        }
        return list;
    }

    @SneakyThrows
    @Transactional(readOnly = true)
    public ObjectNode calculationRefundAmount(String userId) {
        if (isRunning(userId)) {
            throw new ApiException("存在进行中的申请,请勿重复申请");
        }
        UserWalletDTO userWalletDTO = Optional.ofNullable(userWalletService.getByIdDto(userId)).orElseThrow(() -> new ApiException("用户钱包不存在"));
        //充值金额
        Money remainingRechargeAmount = new Money(userWalletDTO.getRemainingRechargeAmount().toString());
        //剩余赠送金额
        Money remainingGiveAmount = new Money(userWalletDTO.getRemainingGiveAmount().toString());
        if (remainingRechargeAmount.compareTo(new Money("0.01")) < 0) {
            throw new ApiException("没有可以退的金额");
        }
        List<UserWalletRefundApplyOrderDTO> orderList = new ArrayList<>();
        String requestId = IdWorker.getIdStr();
        //原路退回
        int refundMethod = 1;
        List<UserWalletRechargeLogDTO> list = userWalletRechargeLogService.refundableOrders(userId);
        if (list.isEmpty()) {
            //线下处理
            refundMethod = 2;
        } else {
            //等于
            UserWalletRechargeLogDTO eq = list.stream().filter(u -> new Money(u.getPayAmount()).compareTo(remainingRechargeAmount) == 0).findFirst().orElse(null);
            if(null ==eq){
                //大于
                UserWalletRechargeLogDTO gt = list.stream().filter(u -> new Money(u.getPayAmount()).compareTo(remainingRechargeAmount) > 0).findFirst().orElse(null);
                if(null ==gt){
                    //总退款金额
                    Money all = remainingRechargeAmount;
                    //已退款金额
                    Money refundAmount = new Money();
                    for (UserWalletRechargeLogDTO userWalletRechargeLogDTO : list) {
                        Money payAmount = new Money(userWalletRechargeLogDTO.getPayAmount());
                        all = all.subtract(payAmount);
                        if (all.compareTo(new Money("0")) > 0) {
                            orderList.add(createOrder(userWalletRechargeLogDTO, requestId, payAmount));
                            refundAmount = refundAmount.add(payAmount);
                        } else {
                            Money subtract = remainingRechargeAmount.subtract(refundAmount);
                            orderList.add(createOrder(userWalletRechargeLogDTO, requestId, subtract));
                            break;
                        }
                    }
                    //退款订单的总金额
                    Money orderRefund = orderList.stream().map(u -> new Money(u.getRefundAmount())).reduce(Money::add).orElse(new Money("0"));
                    if (orderRefund.compareTo(remainingRechargeAmount) < 0) {
                        //说明不够，只能线下退款
                        refundMethod = 2;
                    }
                }else{
                    orderList.add(createOrder(gt, requestId, remainingRechargeAmount));
                }
            }else{
                //有一条相等的订单数据，直接退当前的
                orderList.add(createOrder(eq, requestId, remainingRechargeAmount));
            }
        }
        UserWalletRefundApplyDTO userWalletRefundApplyDTO = new UserWalletRefundApplyDTO();
        userWalletRefundApplyDTO.setRequestId(requestId);
        userWalletRefundApplyDTO.setActualRefundAmount(remainingRechargeAmount.getAmount());
        userWalletRefundApplyDTO.setActualRefundGiveAmount(remainingGiveAmount.getAmount());
        userWalletRefundApplyDTO.setRequestRefundAmount(userWalletRefundApplyDTO.getActualRefundAmount());
        userWalletRefundApplyDTO.setRequestRefundGiveAmount(userWalletRefundApplyDTO.getRequestRefundGiveAmount());
        userWalletRefundApplyDTO.setRefundMethod(refundMethod);
        userWalletRefundApplyDTO.setOrderList(orderList);
        redisTemplateUtil.set(RedisTemplateUtil.getRedisCacheKey(false, PREFIX_APPLY, requestId), objectMapper.writeValueAsString(userWalletRefundApplyDTO), 5, TimeUnit.MINUTES);

        ObjectNode objectNode = objectMapper.createObjectNode();
        objectNode.put("requestId", requestId);
        objectNode.put("refundMethod", refundMethod);
        objectNode.put("requestRefundAmount", remainingRechargeAmount.getAmount().toString());
        objectNode.put("requestRefundGiveAmount", remainingGiveAmount.getAmount().toString());

        userWalletRefundApplyDTO.freeData();
        userWalletDTO.freeData();
        orderList.clear();
        return objectNode;
    }

    private UserWalletRefundApplyOrderDTO createOrder(UserWalletRechargeLogDTO userWalletRechargeLogDTO, String requestId, Money remainingRechargeAmount) {
        UserWalletRefundApplyOrderDTO userWalletRefundApplyOrder = new UserWalletRefundApplyOrderDTO();
        userWalletRefundApplyOrder.setOrderId(userWalletRechargeLogDTO.getOrderId());
        userWalletRefundApplyOrder.setRefundId(IdWorker.getIdStr());
        userWalletRefundApplyOrder.setRequestId(requestId);
        userWalletRefundApplyOrder.setPayAmount(userWalletRechargeLogDTO.getPayAmount());
        userWalletRefundApplyOrder.setRefundAmount(remainingRechargeAmount.getAmount());
        userWalletRefundApplyOrder.setRefundStatus(DisplayUtil.REFUND_STATUS_0);
        return userWalletRefundApplyOrder;
    }


    @Transactional(readOnly = true)
    public boolean isRunning(String userId) {
        final QueryWrapper<UserWalletRefundApply> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("applicant", userId);
        queryWrapper.in("approval_status", FlowConstants.APPROVAL_STATUS_0);
        boolean exists = getBaseMapper().exists(queryWrapper);
        queryWrapper.clear();
        return exists;
    }

    private void createFlow(UserWalletRefundApplyDTO dto) {
        FlowDTO flowDTO = new FlowDTO();
        flowDTO.setBid(dto.getRequestId());
        flowDTO.setOperatorType(OperatorType.INSERT.name());
        flowDTO.setCreateTime(LocalDateTime.now());
        flowDTO.setProcessCode(FlowConstants.PROCESS_CODE_WALLET_REFUND);
        String msg = "钱包退款审核";
        if (dto.getRefundMethod() == 1) {
            msg += ",原路退回";
        } else {
            msg += ",线下处理";
        }
        flowDTO.setTaskName(msg + "【" + dto.getRequestMobile() + "】");
        flowDTO.setProcessUserId(SecurityUtils.getLoginUser().getUserId());
        flowDTO.setProcessUserName(SecurityUtils.getLoginUser().getUsername());
        if (StringUtils.hasLength(SecurityUtils.getLoginUser().getCurrentOrgId())) {
            flowDTO.setProcessOrg(SecurityUtils.getLoginUser().getCurrentOrgId());
            flowDTO.setProcessOrgName(orgService.idToDisplay(SecurityUtils.getLoginUser().getCurrentOrgId()));
        }
        flowService.createFlow(flowDTO);
        flowDTO.freeData();
    }

    @Transactional(rollbackFor = Exception.class)
    public void saveAndApproval(UserWalletRefundApplyDTO dto) {
        dto.setRequestTime(LocalDateTime.now());
        dto.setRequestMobile(SecurityUtils.getLoginUser().getUser().getAppUserExtend().getMobile());
        dto.setApplicant(SecurityUtils.getLoginUser().getUserId());
        dto.setApprovalStatus(FlowConstants.APPROVAL_STATUS_0);
        boolean isUpdate = false;
        UserWalletRefundApply userWalletRefundApply = getById(dto.getRequestId());
        if (null == userWalletRefundApply) {
            String key = RedisTemplateUtil.getRedisCacheKey(false, PREFIX_APPLY, dto.getRequestId());
            String s = redisTemplateUtil.get(key);
            if (!StringUtils.hasLength(s)) {
                throw new ApiException(40004, "请重新查询后申请");
            }
            UserWalletRefundApplyDTO userWalletRefundApplyDTO;
            try {
                userWalletRefundApplyDTO = objectMapper.readValue(s, UserWalletRefundApplyDTO.class);
            } catch (Exception e) {
                throw new ApiException("数据异常，请重新查询后申请");
            }
            if (null != userWalletRefundApplyDTO.getOrderList() && !userWalletRefundApplyDTO.getOrderList().isEmpty()) {
                dto.setOrderList(userWalletRefundApplyDTO.getOrderList());
            }
            if (!Objects.equals(dto.getRefundMethod(), userWalletRefundApplyDTO.getRefundMethod())) {
                throw new ApiException("数据异常，请重新查询后申请");
            }
            if (dto.getRefundMethod() == 2) {
                if (!StringUtils.hasLength(dto.getOpenBank())) {
                    throw new ApiException("开户行不能为空");
                }
                if (!StringUtils.hasLength(dto.getBankCard())) {
                    throw new ApiException("银行卡号不能为空");
                }
            }
            if (dto.getRequestRefundAmount().compareTo(userWalletRefundApplyDTO.getRequestRefundAmount()) != 0) {
                log.error("金额不匹配,缓存金额: {},前端输入: {}", userWalletRefundApplyDTO.getRequestRefundAmount(), dto.getRequestRefundAmount());
                throw new ApiException("数据异常，请重新查询后申请");
            }
            if (null != dto.getActualRefundGiveAmount()) {
                if (dto.getActualRefundGiveAmount().compareTo(userWalletRefundApplyDTO.getRequestRefundGiveAmount()) != 0) {
                    log.error("赠送金额不匹配,缓存金额: {},前端输入: {}", userWalletRefundApplyDTO.getRequestRefundGiveAmount(), dto.getActualRefundGiveAmount());
                    throw new ApiException("数据异常，请重新查询后申请");
                }
            }
            dto.setActualRefundAmount(dto.getRequestRefundAmount());
            dto.setActualRefundGiveAmount(dto.getRequestRefundGiveAmount());
        } else {
            if (userWalletRefundApply.getApprovalStatus().equals(FlowConstants.APPROVAL_STATUS_0)) {
                throw new ApiException("正在审核中，请勿重复提交");
            }
            if (userWalletRefundApply.getApprovalStatus().equals(FlowConstants.APPROVAL_STATUS_1)) {
                throw new ApiException("已经审核通过");
            }
            if (userWalletRefundApply.getApprovalStatus().equals(FlowConstants.APPROVAL_STATUS_2)) {
                throw new ApiException("审核不通过");
            }
            if (userWalletRefundApply.getApprovalStatus().equals(FlowConstants.APPROVAL_STATUS_3)) {
                throw new ApiException("审核被驳回");
            }
            isUpdate = true;
        }
        dto.setApprovalStatus(FlowConstants.APPROVAL_STATUS_0);
        if (isUpdate) {
            update(dto, true);
        } else {
            save(dto);
        }
    }

    public void saveOrders(UserWalletRefundApplyDTO dto) {
        List<UserWalletRefundApplyOrderDTO> orderList = dto.getOrderList();
        if (null != orderList && !orderList.isEmpty()) {
            List<UserWalletRefundApplyOrder> orders = userWalletRefundApplyOrderService.dtoToModelList(orderList);
            userWalletRefundApplyOrderService.saveBatch(orders);
            orders.clear();
            orderList.clear();
        }
    }

    /**
     * 分页数据
     *
     * @param page 分页对象
     * @return 分页信息
     */
    @Transactional(readOnly = true)
    public IPage<FlowDataDTO<UserWalletRefundApplyDTO>> flowPage(Page<FlowDataDTO<UserWalletRefundApplyDTO>> page, UserWalletRefundApplyDTO dto) {
        final QueryWrapper<UserWalletRefundApply> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("t3.approval_depart", SecurityUtils.getLoginUser().getCurrentOrgId());
        if (StringUtils.hasLength(dto.getRequestMobile())) {
            queryWrapper.like("t1.request_mobile", dto.getRequestMobile());
        }
        if (null != dto.getRefundMethod()) {
            queryWrapper.eq("t1.refund_method", dto.getRefundMethod());
        }
        if (StringUtils.hasLength(dto.getBeginTime())) {
            queryWrapper.ge("t1.request_time", dto.getBeginTime() + " 00:00:00");
        }
        if (StringUtils.hasLength(dto.getEndTime())) {
            queryWrapper.le("t1.request_time", dto.getEndTime() + " 23:59:59");
        }
        queryWrapper.orderByAsc("t3.create_time");
        final IPage<FlowDataDTO<UserWalletRefundApplyDTO>> iPage = baseMapper.flowPage(page, queryWrapper);
        Optional.ofNullable(iPage.getRecords()).ifPresent(l -> l.forEach(flow -> buildDisplay(flow.getData(), false)));
        queryWrapper.clear();
        return iPage;
    }

    public void refundRequest(String userId, UserWalletRefundApplyOrder order) {
        final Integer refundNo = 1;
        OrderRefundDTO orderRefund = new OrderRefundDTO();
        orderRefund
                .setRefundNo(refundNo)
                .setThirdPartyOrder(order.getOrderId())
                .setNotifyUrl(pandaParkConfig.getDomain() + "/gy-panda-park/app/userStroke/notify/payAfter")
                .setType(2)
                .setPayAmount(order.getPayAmount())
                .setPersonId(userId)
                .setRefundDesc(PandaConstant.REFUND_DESC_WALLET)
                .setRefundFee(order.getRefundAmount());
        if (!SpringContextHolder.getActiveProfile().equals(SpringContextHolder.PROFILE_DEV)) {
            ApiResultWrapper<Object> resultWrapper = orderHolderService.orderRefund(orderRefund);
            if (!resultWrapper.isSuccess()) {
                throw new ApiException(resultWrapper.getMsg());
            }
        }
        orderRefund.freeData();
    }

    public void refundRequests(LocalDateTime now, String userId, List<UserWalletRefundApplyOrder> orders) {
        List<UserWalletRefundLog> orderRefundLogs = new ArrayList<>(orders.size());
        for (UserWalletRefundApplyOrder order : orders) {
            boolean b = userWalletRefundLogService.isExistsByOrderId(order.getOrderId());
            if (b) {
                log.error("当前退款已经存在，请勿重复申请,orderId: {},refundAmount:{}", order.getOrderId(), order.getRefundAmount().toString());
                throw new ApiException("当前退款已经存在,请勿重复申请");
            }
            UserWalletRefundLog userWalletRefundLog = new UserWalletRefundLog();
            userWalletRefundLog.setUserId(userId);
            userWalletRefundLog.setRefundAmount(order.getRefundAmount());
            userWalletRefundLog.setRefundNo(1);
            userWalletRefundLog.setRefundStatus(DisplayUtil.REFUND_STATUS_1);
            userWalletRefundLog.setRefundTime(now);
            userWalletRefundLog.setRefundId(order.getRefundId());
            userWalletRefundLog.setOrderId(order.getOrderId());
            orderRefundLogs.add(userWalletRefundLog);
            order.setRefundStatus(DisplayUtil.REFUND_STATUS_1);
        }
        userWalletRefundApplyOrderService.updateBatchIds(orders);

        userWalletRefundLogService.saveBatch(orderRefundLogs);
        orderRefundLogs.clear();

        for (UserWalletRefundApplyOrder order : orders) {
            refundRequest(userId, order);
        }
    }

    public void buildExtendsJson(FlowTaskStateDTO flowTaskStateDTO, UserWalletRefundApplyDTO userWalletRefundApplyDTO) {
        //线下处理必须上传打款记录
        JsonNode extendJson = flowTaskStateDTO.getExtendJson();
        if (null == extendJson) {
            throw new ApiException("线下处理请上传退款凭证");
        }
        try {
            List<UserWalletRefundApplyFile> list = objectMapper.readValue(extendJson.toString(), new TypeReference<>() {
            });
            int idx = 1;
            for (UserWalletRefundApplyFile userWalletRefundApplyFile : list) {
                userWalletRefundApplyFile.setRequestId(userWalletRefundApplyDTO.getRequestId());
                userWalletRefundApplyFile.setOperator(SecurityUtils.getLoginUser().getUserId());
                userWalletRefundApplyFile.setOperateOrgId(SecurityUtils.getLoginUser().getCurrentOrgId());
                if (StringUtils.hasLength(userWalletRefundApplyFile.getAttachmentType())) {
                    userWalletRefundApplyFile.setAttachmentType(ConstantDto.ATTACHMENTTYPE_2);
                }
                FileConfirmDTO fileConfirmDTO = getFileConfirmDTO(userWalletRefundApplyFile);
                userWalletRefundApplyFile.setPicPath(fileConfirmDTO.getOfficialFile());
                if (!StringUtils.hasLength(userWalletRefundApplyFile.getPicName())) {
                    userWalletRefundApplyFile.setPicName(fileConfirmDTO.getFileName());
                }
                if (null == userWalletRefundApplyFile.getIdx()) {
                    userWalletRefundApplyFile.setIdx(idx);
                    idx++;
                }
            }
            userWalletRefundApplyFileService.saveBatch(list);
            list.clear();
        } catch (JsonProcessingException e) {
            throw new ApiException("上传退款凭证异常");
        }
    }

    private FileConfirmDTO getFileConfirmDTO(UserWalletRefundApplyFile userWalletRefundApplyFile) {
        FileConfirmDTO fileConfirmDTO = FileConfirmUtil.singleFileConfirm(userWalletRefundApplyFile.getPicPath(), ConstantDto.ATTACHMENTTYPE_2);
        if (null == fileConfirmDTO) {
            throw new ApiException("上传文件ID值错误");
        }
        return fileConfirmDTO;
    }

    @Transactional(readOnly = true)
    public UserWalletRefundApply sumRefundByUserId(String userId) {
        QueryWrapper<UserWalletRefundApply> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("applicant", userId);
        queryWrapper.eq("approval_status", FlowConstants.APPROVAL_STATUS_1);
        queryWrapper.select("applicant,IFNULL(sum(actual_refund_amount),0.00) AS actual_refund_amount,"
                + "IFNULL(sum(actual_refund_give_amount),0.00) AS actual_refund_give_amount");
        UserWalletRefundApply userWalletRefundApply = baseMapper.selectOne(queryWrapper, false);
        queryWrapper.clear();
        if (null == userWalletRefundApply) {
            userWalletRefundApply = new UserWalletRefundApply();
            userWalletRefundApply.setActualRefundAmount(new BigDecimal("0.00"));
            userWalletRefundApply.setActualRefundGiveAmount(userWalletRefundApply.getActualRefundAmount());
        }
        return userWalletRefundApply;
    }

    @Transactional(readOnly = true)
    public UserWalletRefundApplyStatisticsDTO sumAmount(UserWalletRefundApplyDTO dto) {
        final QueryWrapper<UserWalletRefundApply> queryWrapper = createQueryWrapper(dto);
        UserWalletRefundApplyStatisticsDTO userWalletRefundApplyStatisticsDTO = baseMapper.sumAmount(queryWrapper);
        queryWrapper.clear();
        if (null == userWalletRefundApplyStatisticsDTO) {
            userWalletRefundApplyStatisticsDTO = new UserWalletRefundApplyStatisticsDTO();
            userWalletRefundApplyStatisticsDTO.setRequestRefundAmount(new BigDecimal("0.00"));
            userWalletRefundApplyStatisticsDTO.setRequestRefundGiveAmount(userWalletRefundApplyStatisticsDTO.getRequestRefundAmount());
            userWalletRefundApplyStatisticsDTO.setActualRefundAmount(userWalletRefundApplyStatisticsDTO.getRequestRefundAmount());
            userWalletRefundApplyStatisticsDTO.setActualRefundGiveAmount(userWalletRefundApplyStatisticsDTO.getRequestRefundAmount());
            userWalletRefundApplyStatisticsDTO.setQuantity(0L);
        }
        return userWalletRefundApplyStatisticsDTO;
    }
}